home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 11
/
CU Amiga Magazine's Super CD-ROM 11 (1997)(EMAP Images)(GB)(Track 1 of 3)[!][issue 1997-06].iso
/
cucd
/
programming
/
oberonv4
/
source
/
system
/
xe.mod
(
.txt
)
< prev
next >
Wrap
Oberon Text
|
1996-04-09
|
57KB
|
1,171 lines
Syntax10.Scn.Fnt
Syntax10i.Scn.Fnt
StampElems
Alloc
9 Apr 96
Syntax12i.Scn.Fnt
FoldElems
LineElems
Alloc
Syntax10m.Scn.Fnt
Syntax10b.Scn.Fnt
MODULE XE; (** SHML 10 Dec 90,
(** eXtended Edit: Supports various enhancements over usual TextFrames.Handle for programmer's purposes *)
(* Created by Stefan H._M. Ludwig, Institute for Computer Systems, ETH Zurich, ludwig@inf.ethz.ch, 10 Dec 90
Changes:
5 Jan 96: SHML - added cleanup task to fix a text after a trap occurred during compilation
new: cleanup, PrepareText, RestoreText
8 Jan 96: SHML - fixed bug in RestoreText
new: viewerComp
17 Jan 96: SHML - XE.Comp of a list of files will stop after first error, open the file, and mark the errors in it
new: CheckErrors, OpenModViewer
31 Jan 96: SHML - selected text remains selected when an UpdateMsg with id Texts.replace is sent (e.g. change font
no longer clears selection).
new: keepSel in Handle
2 Feb 96: SHML - restructured configuration of menu frame, change command names
changed: Err -> Error, Comp -> Compile
new: DefMenu(Num), TextMenu(Num), ToolMenu(Num), WideMenu(Num), AsciiMenu(Num)
14 Feb 96: SHML - XE.Compile <filelist> generates compiler command from file extension,
InNameSet/TrackSelection accept "@" as part of a name
7 Mar 96: SHML - double left click clears selection. Useful for Edit.Search, when something is changed
and the selection remains, but it's not the searchable selection.
new: lastCarSet
11 Mar 96: SHML - added colon ":" as a valid filename content to the Amiga section
3 Apr 96: SHML - Uses module Host. Fully portable.
removed: InFileNameSet, HostDependentStuff, OptionChar1, OptionChar2
(* Declarations *)
IMPORT Host, Modules, Display, Input, Files, Fonts, Texts, Viewers, Oberon, TextFrames, MenuViewers, FoldElems;
CONST
GetHandlerKey* = -210566; (** secret number to get XE.Handle *)
WordBoundary* = 0; NameBoundary* = 1; FileNameBoundary* = 2; (** type for WordBounds checking *)
DefErrFile = "OberonErrors.Text"; ErrFont = "Syntax8.Scn.Fnt";
ML = 2; MM = 1; MR = 0;
CtrlB = 2X; CtrlD = 4X; CtrlE = 5X; CtrlF = 6X; BS = 08X; LF = 0AX; CtrlK = 0BX; CR = 0DX; CtrlN = 0EX;
CtrlP = 10X; CtrlT = 14X; CtrlW = 17X; CtrlX = 18X; CtrlZ = 1AX;
UpArrow = 0C1X; DnArrow = 0C2X;
MaxPat = 32;
Version = "XE (SHML 9 Apr 96)";
XEMenu = "XE.Menu.Text"; EditMenu = "Edit.Menu.Text"; SystemMenu = "System.Menu.Text";
ConfigurationName = "XE.Configuration.Text";
KeyHandler = "EditKeys.GetKeyHandler";
DefComp = "Compiler.Compile"; (* default compiler command *)
DefOpenCmd = "Doc.Open"; DefOpenCmd1 = "XE.Open"; (* commands used by OpenCall *)
Empty0 = "Empty.Mod"; Empty1 = "Empty.Tool"; Empty3 = "Empty.c"; (* default empty files for Defaults *)
Ext00 = "Mod"; Ext01 = "Text"; Ext1 = "Tool"; Ext30 = "c"; Ext31 = "h"; (* default file extensions for Defaults *)
AsciiFont = "Courier10.Scn.Fnt"; (* used by OpenAscii for displaying ascii texts *)
DefMenuNum = 0; TextMenuNum = 1; ToolMenuNum = 2; WideMenuNum = 3; AsciiMenuNum = 4;
DefMenu = "System.Close System.Copy System.Grow Edit.Search Edit.Replace All Edit.Parcs XE.Compile XE.Error Edit.Store ";
TextMenu = "System.Close System.Copy System.Grow Edit.Search Edit.Replace All Edit.Parcs Edit.Store ";
ToolMenu = "System.Close System.Grow Edit.Parcs Edit.Store ";
WideMenu = "System.Close System.Copy System.Grow Edit.Search Edit.Replace All Edit.Parcs XE.Compile XE.Error Edit.Store | Log.Open | XE.SysOpen Strip ";
AsciiMenu = "System.Close System.Copy System.Grow Edit.Search Edit.Replace All XE.Compile EditTools.LocateLine ^ EdiT.StoreAscii ";
TYPE
LongName = ARRAY 128 OF CHAR;
Name = ARRAY 32 OF CHAR;
Elem = POINTER TO ElemDesc;
ElemDesc = RECORD (Texts.ElemDesc)
err: INTEGER;
pos: LONGINT;
wide: BOOLEAN;
num: ARRAY 8 OF CHAR;
msg: LongName
END;
Element = POINTER TO ElementDesc;
ElementDesc = RECORD
compiler, ext: Name; errFile: LongName;
next: Element
END;
wr: Texts.Writer;
errT: Texts.Text; errFnt: Fonts.Font;
keyHandle: Display.Handler;
cleanup: RECORD (* exception handling, if traps occur during compilation *)
text: Texts.Text;
oldNotify: Texts.Notifier;
task: Oberon.Task
END;
compiler, defComp, openCmd: Name;
empty: ARRAY 4 OF Name;
ext: ARRAY 4, 2 OF Name;
first, viewerComp: BOOLEAN;
delay, lastCarSet: LONGINT;
root: Element;
find: RECORD
len: SHORTINT;
buf: ARRAY MaxPat OF CHAR;
shiftTab: ARRAY 256 OF SHORTINT
END;
(* Support *)
PROCEDURE Str(s: ARRAY OF CHAR); BEGIN Texts.WriteString(wr, s) END Str;
PROCEDURE Ch(ch: CHAR); BEGIN Texts.Write(wr, ch) END Ch;
PROCEDURE Ln; BEGIN Texts.WriteLn(wr); Texts.Append(Oberon.Log, wr.buf) END Ln;
PROCEDURE Extension(name: ARRAY OF CHAR; VAR ext: ARRAY OF CHAR); (* get extension of name *)
VAR i, j: INTEGER;
BEGIN
i := -1;
REPEAT INC(i) UNTIL name[i] = 0X;
REPEAT DEC(i) UNTIL (name[i] = ".") OR (i = 0);
IF i = 0 THEN ext[0] := 0X
ELSE
j := -1;
REPEAT INC(i); INC(j); ext[j] := name[i] UNTIL name[i] = 0X
END
END Extension;
PROCEDURE Append(src: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR); (* append src to dest if no "." in src *)
VAR i, off: INTEGER;
BEGIN
off := -1;
REPEAT INC(off) UNTIL (dest[off] = 0X) OR (dest[off] = ".");
IF dest[off] # "." THEN
i := -1;
REPEAT INC(i); dest[i+off] := src[i] UNTIL src[i] = 0X END
END Append;
PROCEDURE SearchPair(ext: ARRAY OF CHAR; VAR prev: Element): Element;
VAR l: Element;
BEGIN
l := root; prev := NIL;
WHILE (l # NIL) & (l.ext # ext) DO prev := l; l := l.next END;
RETURN l
END SearchPair;
PROCEDURE ScanFirst(VAR s: Texts.Scanner); (* Scan first parameter *)
VAR sel: Texts.Text; beg, end, time: LONGINT;
BEGIN
Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s);
IF (s.class = Texts.Char) & (s.line = 0) & (s.c = "^") THEN
Oberon.GetSelection(sel, beg, end, time);
IF time >= 0 THEN Texts.OpenScanner(s, sel, beg); Texts.Scan(s) END
END
END ScanFirst;
PROCEDURE InstallKeyHandler;
VAR save, par: Oberon.ParList; res: INTEGER;
BEGIN
save := Oberon.Par;
NEW(par); NEW(par.frame); par.frame.X := 0; par.frame.Y := 0; par.pos := -42; (* magic *)
Oberon.Call(KeyHandler, par, FALSE, res);
IF res = 0 THEN keyHandle := Oberon.Par.frame.handle
ELSE keyHandle := NIL
END;
Oberon.Par := save; Modules.res := 0 (* bug in Modules? *)
END InstallKeyHandler;
PROCEDURE MenuFrame*(name, menu: ARRAY OF CHAR; line: INTEGER): TextFrames.Frame;
(** open XEMenu/EditMenu/SystemMenu and if existant get lineth textline (counting starts with 0) as menuline; (line >= 0, 100) *)
VAR
mf: TextFrames.Frame; buf: Texts.Buffer; t: Texts.Text;
r: Texts.Reader; start, end: LONGINT; ch: CHAR; menuFile: LongName;
BEGIN
ASSERT(line >= 0, 100);
IF Files.Old(XEMenu) # NIL THEN menuFile := XEMenu
ELSIF (line = 1) & (Files.Old(SystemMenu) # NIL) THEN menuFile := SystemMenu
ELSIF Files.Old(EditMenu) # NIL THEN menuFile := EditMenu
ELSE RETURN TextFrames.NewMenu(name, menu)
END;
NEW(t); Texts.Open(t, menuFile);
Texts.OpenReader(r, t, 0);
REPEAT (* skip line lines *)
start := Texts.Pos(r);
REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = 0DX);
DEC(line)
UNTIL line = -1;
IF r.eot THEN end := t.len ELSE end := Texts.Pos(r)-1 END;
IF start = end THEN RETURN TextFrames.NewMenu(name, menu)
ELSE
NEW(buf); Texts.OpenBuf(buf); Texts.Save(t, start, end, buf);
mf := TextFrames.NewMenu(name, ""); Texts.Append(mf.text, buf);
RETURN mf
END
END MenuFrame;
PROCEDURE OpenText(VAR t: Texts.Text; VAR name: ARRAY OF CHAR;
scanName: ARRAY OF CHAR; scanClass: INTEGER; default, ext1, ext2: ARRAY OF CHAR);
VAR extName: LongName; i, len: INTEGER;
PROCEDURE Extend(VAR str: ARRAY OF CHAR; with: ARRAY OF CHAR); (* extend str with with *)
VAR ls, le: INTEGER;
BEGIN
ls := -1;
REPEAT INC(ls) UNTIL str[ls] = 0X;
le := -1;
REPEAT INC(le) UNTIL with[le] = 0X;
IF ls <= LEN(str)-(le+1) THEN
INC(ls, le+1);
REPEAT str[ls] := with[le]; DEC(ls); DEC(le) UNTIL le = -1;
str[ls] := "."
END
END Extend;
PROCEDURE Try(): BOOLEAN; (* try opening name with ext1 or ext2 appended to it *)
BEGIN
COPY(name, extName); Extend(extName, ext1); t := TextFrames.Text(extName);
IF t.len = 0 THEN COPY(name, extName); Extend(extName, ext2); t := TextFrames.Text(extName) END;
RETURN t.len > 0
END Try;
BEGIN
IF first THEN first := FALSE; Str(Version); Ln; InstallKeyHandler END; (* write a startup message to the Log (once) *)
find.len := 0;
IF scanClass = Texts.String THEN
t := TextFrames.Text(scanName);
name[0] := '"'; i := 0;
REPEAT INC(i); name[i] := scanName[i-1] UNTIL name[i] = 0X;
name[i] := '"'; name[i+1] := 0X
ELSIF scanClass # Texts.Name THEN t := TextFrames.Text(default); COPY(default, name)
ELSE
COPY(scanName, name); t := TextFrames.Text(name); (* use original name *)
IF t.len = 0 THEN (* name doesn't exist *)
IF Try() THEN COPY(extName, name) (* use extended name *)
ELSE
len := -1;
REPEAT INC(len) UNTIL scanName[len] = 0X;
REPEAT DEC(len) UNTIL (name[len] = ".") OR (len = 0);
IF len # 0 THEN (* name[len] = "." *)
i := -1; (* copy appended name to pattern for Edit.Show *)
REPEAT INC(i); find.buf[i] := name[i+len+1] UNTIL find.buf[i] = 0X;
find.len := SHORT(i);
name[len] := 0X; (* delete extension, try with trimmed name *)
IF Try() THEN COPY(extName, name) (* use extended name *)
ELSE COPY(scanName, name) (* use original name with empty text *)
END
END
END
END
END
END OpenText;
PROCEDURE Show(f: TextFrames.Frame; pos: LONGINT);
VAR end, delta: LONGINT;
BEGIN
delta := 200; end := TextFrames.Pos(f, f.X+f.W, f.Y);
WHILE ((f.org > pos) OR (pos >= end)) & (f.org # end) DO
TextFrames.Show(f, pos-delta); DEC(delta, 20);
end := TextFrames.Pos(f, f.X+f.W, f.Y)
END
END Show;
PROCEDURE GetOptions(VAR s: Texts.Scanner; VAR options: ARRAY OF CHAR);
VAR pos: LONGINT; i: INTEGER; ch: CHAR; r: Texts.Reader;
BEGIN
IF (s.class # Texts.Char) OR (s.c # Host.OptionChar) THEN options[0] := 0X
ELSE
pos := Texts.Pos(s);
options[0] := s.c; ch := s.nextCh; i := 1; r := s;
WHILE ((ch >= "0") & (ch <= "9") OR (ch >= "a") & (ch <= "z")) & (i < LEN(options)-1) DO
options[i] := ch; INC(i); Texts.Read(r, ch)
END;
options[i] := 0X; pos := pos+(i-1);
WHILE Texts.Pos(s) < pos DO Texts.Scan(s) END; Texts.Scan(s)
END
END GetOptions;
PROCEDURE NoNotify(t: Texts.Text; op: INTEGER; beg, end: LONGINT);
END NoNotify;
PROCEDURE PrepareText(t: Texts.Text);
BEGIN
cleanup.text := t; cleanup.oldNotify := t.notify; t.notify := NoNotify;
FoldElems.ExpandAll(t, 0, TRUE);
Oberon.Install(cleanup.task)
END PrepareText;
PROCEDURE RestoreText;
BEGIN
Oberon.Remove(cleanup.task);
FoldElems.CollapseAll(cleanup.text, {FoldElems.tempLeft});
cleanup.text.notify := cleanup.oldNotify;
IF ~viewerComp THEN cleanup.text.notify(cleanup.text, Texts.replace, 0, cleanup.text.len) END
END RestoreText;
PROCEDURE BackRead*(VAR r(*inout*): Texts.Reader; t: Texts.Text; VAR ch(*out*): CHAR);
VAR p: LONGINT;
BEGIN
p := Texts.Pos(r);
IF p > 0 THEN Texts.OpenReader(r, t, p-1); Texts.Read(r, ch); Texts.OpenReader(r, t, p-1)
ELSE ch := 0X
END
END BackRead;
PROCEDURE InWordSet(ch: CHAR): BOOLEAN;
BEGIN
RETURN (("0" <= ch) & (ch <= "9") OR ("A" <= ch) & (ch <= "Z")
OR ("a" <= ch) & (ch <= "z") OR (80X <= ch) & (ch <= 95X))
END InWordSet;
PROCEDURE InNameSet(ch: CHAR): BOOLEAN;
BEGIN RETURN InWordSet(ch) OR (ch = ".") OR (ch = "@")
END InNameSet;
PROCEDURE PrepareFind; (* prepare find.shiftTab according to current pattern *)
VAR i: INTEGER; m: SHORTINT;
BEGIN
m := find.len+1;
FOR i := 0 TO LEN(find.shiftTab)-1 DO find.shiftTab[i] := m END; (* init all chars with length of pattern+1 *)
DEC(m);
FOR i := 0 TO find.len-1 DO
find.shiftTab[ORD(find.buf[i])] := m;
DEC(m)
END
END PrepareFind;
PROCEDURE Find(t: Texts.Text; beg: LONGINT; VAR end: LONGINT); (*<< Quicksearch algorithm, D.M. Sunday *)
VAR r: Texts.Reader; i: INTEGER; found: BOOLEAN; ch: CHAR;
BEGIN
found := FALSE;
REPEAT
Texts.OpenReader(r, t, beg); Texts.Read(r, ch); i := 0;
WHILE ~r.eot & (i < find.len) & (ch = find.buf[i]) DO Texts.Read(r, ch); INC(i) END;
IF ~r.eot & (i < find.len) THEN
Texts.OpenReader(r, t, beg+find.len); Texts.Read(r, ch);
beg := beg+find.shiftTab[ORD(ch)]
ELSE found := TRUE
END
UNTIL found;
IF i = find.len THEN end := beg+find.len ELSE end := -1 END
END Find;
PROCEDURE SearchIdent(f: TextFrames.Frame);
VAR t: Texts.Text; pos, dec, start: LONGINT; r: Texts.Reader; ch: CHAR;
BEGIN
t := f.text;
IF find.len > 0 THEN (* simulate Edit.Show *)
dec := 1;
find.buf[find.len] := "*"; INC(find.len); find.buf[find.len] := 0X; (* search for name* *)
PrepareFind; Find(t, 0, pos);
IF pos = -1 THEN (* not found *)
find.buf[find.len-1] := "-"; PrepareFind; Find(t, 0, pos); (* search for name- *)
IF pos = -1 THEN (* not found *)
DEC(find.len); find.buf[find.len] := 0X; start := 0; dec := 0; (* search for name *)
PrepareFind;
REPEAT
Find(t, start, pos);
IF pos > 0 THEN (* something found, check if it's an identifier. If not, search again *)
Texts.OpenReader(r, t, pos); Texts.Read(r, ch);
IF ("0" <= ch) & (ch <= "9") OR ("A" <= ch) & (ch <= "Z") OR ("a" <= ch) & (ch <= "z") THEN
start := pos
ELSE start := -1
END
ELSE start := -1
END
UNTIL start = -1;
(*DEC(find.len); find.buf[find.len] := 0X; Find(t, 0, pos); dec := 0*)
END
END;
IF pos > 0 THEN
pos := pos-find.len;
TextFrames.Show(f, pos); TextFrames.SetSelection(f, pos, pos+find.len-dec);
Oberon.PassFocus(Viewers.This(f.X, f.Y)); TextFrames.SetCaret(f, pos+find.len-dec)
END
END
END SearchIdent;
(* Text Support *)
PROCEDURE WordBounds*(t: Texts.Text; VAR beg(*inout*), end(*out*): LONGINT; type: INTEGER);
(** locate the word bounds [beg, end[ in text t starting at beg;
(type IN {WordBoundary, NameBoundary, FileNameBoundary}, 100) *)
VAR r: Texts.Reader; ch: CHAR; word, name, fileName: BOOLEAN;
BEGIN
ASSERT(type IN {WordBoundary, NameBoundary, FileNameBoundary}, 100);
word := type = WordBoundary;
name := type = NameBoundary;
fileName := type = FileNameBoundary;
Texts.OpenReader(r, t, beg);
REPEAT Texts.Read(r, ch)
UNTIL r.eot OR word & ~InWordSet(ch) OR name & ~InNameSet(ch) OR fileName & ~Host.IsFileNameChar(ch);
IF r.eot THEN end := t.len ELSE end := Texts.Pos(r)-1 END;
Texts.OpenReader(r, t, beg);
REPEAT BackRead(r, t, ch)
UNTIL word & ~InWordSet(ch) OR name & ~InNameSet(ch) OR fileName & ~Host.IsFileNameChar(ch);
IF ch = 0X THEN beg := 0 ELSE beg := Texts.Pos(r)+1 END
END WordBounds;
PROCEDURE EndOfLine*(f: TextFrames.Frame; VAR loc(*inout*): TextFrames.Location;
org: LONGINT; VAR end(*inout*): LONGINT);
(** locate end of line starting at loc and end *)
BEGIN
WHILE (end < f.text.len) & (loc.org <= org) DO INC(end, 30); TextFrames.LocatePos(f, end, loc) END;
IF (end >= f.text.len) & (loc.org <= org) THEN end := f.text.len
ELSE WHILE loc.org > org DO end := loc.org; TextFrames.LocatePos(f, end-1, loc) END
END
END EndOfLine;
PROCEDURE TrackSelection*(f: TextFrames.Frame; VAR x(*inout*), y(*inout*): INTEGER; VAR keySum(*inout*): SET);
VAR
keys: SET;
beg, end, begW, endW, begN, endN, pos: LONGINT; loc, loc1: TextFrames.Location;
v: Viewers.Viewer; upper: TextFrames.Frame;
r: Texts.Reader; ch: CHAR;
BEGIN
v := Viewers.This(f.X, f.Y); v := v.next(Viewers.Viewer);
IF (v.dsc # NIL) & (v.dsc.next # NIL) & (v.dsc.next IS TextFrames.Frame) THEN
upper := v.dsc.next(TextFrames.Frame);
IF upper.hasSel & (upper.text = f.text) THEN
TextFrames.LocateLine(upper, upper.bot, loc);
IF (upper.selbeg.pos < loc.org) & (upper.org < upper.selend.pos)
& (upper.selbeg.pos <= TextFrames.Pos(f, x, y)) THEN
TextFrames.SetSelection(f, upper.selbeg.pos, TextFrames.Pos(f, x, y)+1)
ELSE TextFrames.RemoveSelection(upper); upper := NIL
END
ELSE upper := NIL
END
ELSE upper := NIL
END;
IF upper = NIL THEN
pos := TextFrames.Pos(f, x, y);
IF f.hasSel & (Oberon.Time() < f.time+delay) THEN
beg := f.selbeg.pos; end := f.selend.pos;
IF (beg+1 = end) & (pos = beg) THEN (* one char selected, mouse on same character *)
TextFrames.LocatePos(f, beg, loc); TextFrames.LocatePos(f, end, loc1);
Texts.OpenReader(r, f.text, beg); Texts.Read(r, ch);
IF (end = f.text.len) OR (loc.org # loc1.org) OR ~InNameSet(ch) THEN (* extend to whole line *)
EndOfLine(f, loc1, loc.org, end);
TextFrames.SetSelection(f, loc.org, end)
ELSE (* (end # f.text.len) & (loc.org = loc1.org) & InNameSet(ch) *)
begW := pos; endW := pos+1;
IF (ch = ".") OR (ch = "@") THEN WordBounds(f.text, begW, endW, NameBoundary)
ELSE WordBounds(f.text, begW, endW, WordBoundary)
END;
begN := pos; endN := pos+1; WordBounds(f.text, begN, endN, NameBoundary);
IF (begW = beg) & (endW = end) THEN
IF (begN = beg) & (endN = end) THEN
(* single char InNameSet -> select line *)
EndOfLine(f, loc1, loc.org, end);
TextFrames.SetSelection(f, loc.org, end)
ELSE TextFrames.SetSelection(f, begN, endN) (* name *)
END
ELSE TextFrames.SetSelection(f, begW, endW) (* word *)
END
END
ELSIF (beg <= pos) & (pos < end) THEN (* mouse within selection *)
TextFrames.LocatePos(f, beg, loc); TextFrames.LocatePos(f, end-1, loc1);
IF loc.org = loc1.org THEN (* selection is at most one line *)
begW := pos; endW := pos+1; WordBounds(f.text, begW, endW, WordBoundary);
begN := pos; endN := pos+1; WordBounds(f.text, begN, endN, NameBoundary);
IF (begW = beg) & (endW = end) & ((begN < beg) OR (end < endN)) THEN
(* word selected -> extend to name *)
TextFrames.SetSelection(f, begN, endN)
ELSE (* name selected -> extend to line *)
endN := loc1.pos; EndOfLine(f, loc1, loc.org, endN);
IF (loc.org # beg) OR (endN # end) THEN TextFrames.SetSelection(f, loc.org, endN)
ELSE TextFrames.SetSelection(f, pos, pos+1) (* select single char *)
END
END
ELSE TextFrames.SetSelection(f, pos, pos+1) (* not same line -> select single char *)
END
ELSE TextFrames.SetSelection(f, pos, pos+1) (* not within selection -> select single char *)
END
ELSE TextFrames.SetSelection(f, pos, pos+1) (* no selection or time out -> select single char *)
END; (* f.hasSel & ... *)
end := f.selend.pos
ELSE end := upper.selbeg.pos
END; (* upper = NIL *)
REPEAT
Input.Mouse(keys, x, y); keySum := keySum+keys; Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y);
pos := TextFrames.Pos(f, x, y)+1;
IF f.hasSel THEN
IF pos >= end THEN TextFrames.SetSelection(f, f.selbeg.pos, pos);
IF upper # NIL THEN
TextFrames.SetSelection(upper, upper.selbeg.pos, pos); upper.selend.pos := f.selend.pos
END
END
ELSE TextFrames.SetSelection(f, TextFrames.Pos(f, x, y), TextFrames.Pos(f, x, y)+1)
END
UNTIL keys = {};
IF upper # NIL THEN f.selbeg.pos := upper.selbeg.pos END
END TrackSelection;
PROCEDURE CaretVisible(f: TextFrames.Frame; pos: LONGINT): BOOLEAN;
BEGIN RETURN f.hasCar & (f.carloc.y >= f.bot) & (f.carloc.pos = pos)
END CaretVisible;
PROCEDURE MoveTextStretch(from: Texts.Text; to: TextFrames.Frame; beg, end, pos: LONGINT);
VAR len: LONGINT;
BEGIN
(* only if other text or target pos not within selection *)
IF ((from # to.text) OR (pos < beg) OR (end < pos)) THEN
len := end-beg;
IF (from = to.text) & (end < pos) THEN DEC(pos, len) END; (* dec caret pos by length of sel *)
Texts.Save(from, beg, end, wr.buf); Texts.Delete(from, beg, end); Texts.Insert(to.text, pos, wr.buf);
TextFrames.SetCaret(to, pos+len);
IF CaretVisible(to, pos+len) THEN TextFrames.SetSelection(to, pos, pos+len) END
END
END MoveTextStretch;
PROCEDURE MoveSelection(f: TextFrames.Frame; x, y: INTEGER; keySum: SET; VAR done: BOOLEAN);
VAR keys: SET; v: Viewers.Viewer; target: TextFrames.Frame; time: LONGINT; oldX, oldY: INTEGER;
BEGIN
time := Oberon.Time(); oldX := x; oldY := y;
REPEAT Input.Mouse(keys, x, y); keySum := keySum+keys; Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y)
UNTIL (keys = {}) OR (keySum # {MM})
OR (ABS(oldX-x) < 3) & (ABS(oldY-y) < 3) & (Oberon.Time() > time+delay DIV 2);
IF (keys # {}) & (keySum = {MM, ML}) THEN
v := Viewers.This(x, y);
IF (v.dsc # NIL) & (v.dsc.next # NIL) & (v.dsc.next IS TextFrames.Frame) THEN
target := v.dsc.next(TextFrames.Frame);
Oberon.PassFocus(v); TextFrames.TrackCaret(target, x, y, keySum);
IF keySum = {MM, ML} THEN
MoveTextStretch(f.text, target, f.selbeg.pos, f.selend.pos, target.carloc.pos)
END
END;
done := TRUE
ELSE done := FALSE
END
END MoveSelection;
PROCEDURE OpenCall(f: TextFrames.Frame; x, y: INTEGER; pos: LONGINT);
VAR s: Texts.Scanner; par: Oberon.ParList; loc: TextFrames.Location; beg, end, newPos: LONGINT; res: INTEGER;
BEGIN
TextFrames.LocateChar(f, x, y, loc); newPos := loc.pos;
REPEAT beg := newPos; WordBounds(f.text, beg, end, FileNameBoundary); DEC(newPos)
UNTIL (beg < end) OR (newPos < pos);
IF beg < end THEN
Texts.OpenScanner(s, f.text, beg); Texts.Scan(s);
IF (s.line = 0) & (s.class = Texts.Name) THEN
NEW(par); par.frame := f; par.text := f.text; par.pos := beg;
Oberon.Call(openCmd, par, FALSE, res);
IF res # 0 THEN Oberon.Call(DefOpenCmd1, par, FALSE, res) END
END
END
END OpenCall;
(** Error Element *)
PROCEDURE ElemWidth(e: Elem): INTEGER;
VAR pat: Display.Pattern; i, px, dx, x, y, w, h: INTEGER; str: LongName;
BEGIN
i := 0; px := 0;
IF e.wide THEN COPY(e.msg, str) ELSE COPY(e.num, str) END;
WHILE str[i] # 0X DO
Display.GetChar(errFnt.raster, str[i], dx, x, y, w, h, pat); INC(px, dx); INC(i)
END;
RETURN px+6
END ElemWidth;
PROCEDURE UpdateErr(e: Elem);
VAR t: Texts.Text;
BEGIN (* precondition: e.pos is correct *)
t := Texts.ElemBase(e); t.notify(t, Texts.replace, e.pos, e.pos+1)
END UpdateErr;
PROCEDURE ShowErrMsg(e: Elem; col: SHORTINT; x0, y0, dw: INTEGER);
VAR
pat: Display.Pattern; i, px, rm, dx, x, y, w, h: INTEGER;
ch: CHAR; str: LongName;
BEGIN
IF e.wide THEN COPY(e.msg, str) ELSE COPY(e.num, str) END;
i := 0; px := x0+3; rm := x0+dw-3; INC(y0, 2);
LOOP
ch := str[i]; INC(i);
IF ch = 0X THEN EXIT END;
Display.GetChar(errFnt.raster, ch, dx, x, y, w, h, pat);
IF px+dx > rm THEN EXIT END;
Display.CopyPattern(col, pat, px+x, y0+y, Display.invert); INC(px, dx)
END
END ShowErrMsg;
PROCEDURE DeleteErrElems*(t: Texts.Text);
VAR r: Texts.Reader; pos: LONGINT;
BEGIN
Texts.OpenReader(r, t, 0); Texts.ReadElem(r);
WHILE r.elem # NIL DO
IF r.elem IS Elem THEN pos := Texts.Pos(r); Texts.Delete(t, pos-1, pos); Texts.OpenReader(r, t, pos) END;
Texts.ReadElem(r)
END
END DeleteErrElems;
PROCEDURE ElemHandle(e: Texts.Elem; VAR msg: Texts.ElemMsg);
VAR copy: Elem; w, h: INTEGER; keys, keySum: SET;
PROCEDURE Expand(el: Elem);
VAR s: Texts.Scanner; n: INTEGER; ch: CHAR;
BEGIN
IF el.msg[0] = 0X THEN
Texts.OpenScanner(s, errT, 0);
REPEAT
s.line := 0;
REPEAT Texts.Scan(s) UNTIL s.eot OR (s.line # 0)
UNTIL s.eot OR (s.class = Texts.Int) & (s.i = el.err);
IF ~s.eot THEN
Texts.Read(s, ch); n := 0;
WHILE ~s.eot & (ch # CR) & (n+1 < LEN(el.msg)) DO el.msg[n] := ch; INC(n); Texts.Read(s, ch) END;
el.msg[n] := 0X
ELSE el.msg := "no message found"
END
END;
el.wide := TRUE;
el.W := LONG(ElemWidth(el))*TextFrames.Unit
END Expand;
BEGIN
WITH e: Elem DO
WITH msg: TextFrames.DisplayMsg DO
IF ~msg.prepare THEN
w := SHORT(e.W DIV TextFrames.Unit); h := SHORT(e.H DIV TextFrames.Unit);
Display.ReplConst(Display.white, msg.X0+1, msg.Y0+2, w-2, h, Display.replace);
ShowErrMsg(e, msg.col, msg.X0, msg.Y0+2, w)
END
| msg: TextFrames.TrackMsg DO (* a mouse click hit the element *)
IF msg.keys = {MM} THEN
w := SHORT(e.W DIV TextFrames.Unit); h := SHORT(e.H DIV TextFrames.Unit);
Oberon.RemoveMarks(msg.X0, msg.Y0, w, h);
Display.ReplConst(Display.white, msg.X0+2, msg.Y0+3, w-4, h-2, Display.invert);
keySum := msg.keys;
REPEAT
Input.Mouse(keys, msg.X, msg.Y); keySum := keySum+keys;
Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, msg.X, msg.Y)
UNTIL keys = {};
Display.ReplConst(Display.white, msg.X0+2, msg.Y0+3, w-4, h-2, Display.invert);
e.pos := msg.pos;
IF keySum = {MM} THEN (* expand/reduce element *)
IF ~e.wide THEN Expand(e)
ELSE e.wide := FALSE; e.W := LONG(ElemWidth(e))*TextFrames.Unit
END;
UpdateErr(e)
END;
msg.keys := {}
END
| msg: Texts.CopyMsg DO (* copy element *)
NEW(copy); Texts.CopyElem(e, copy); copy.err := e.err; copy.pos := e.pos; copy.wide := e.wide;
copy.num := e.num; copy.msg := e.msg; msg.e := copy
ELSE
END
END
END ElemHandle;
PROCEDURE InsertErrAt*(t: Texts.Text; pos: LONGINT; err: INTEGER);
(** insert an error element into text t at position pos marking error err *)
VAR e: Elem; h: ARRAY 8 OF CHAR; j, k: INTEGER;
BEGIN
NEW(e); e.H := 3*TextFrames.mm; e.handle := ElemHandle;
e.err := err; e.msg := ""; e.wide := FALSE;
k := 0;
REPEAT h[k] := CHR(err MOD 10 + ORD("0")); err := err DIV 10; INC(k) UNTIL err = 0;
j := 0;
REPEAT DEC(k); e.num[j] := h[k]; INC(j) UNTIL k = 0;
e.num[j] := 0X;
e.W := LONG(ElemWidth(e))*TextFrames.Unit;
Texts.WriteElem(wr, e); Texts.Insert(t, pos, wr.buf)
END InsertErrAt;
PROCEDURE CheckErrors*(VAR s(*out*): Texts.Scanner; logPos: LONGINT): BOOLEAN;
(** are there error messages in the log text starting at logPos ? *)
BEGIN
Texts.OpenScanner(s, Oberon.Log, logPos);
REPEAT Texts.Scan(s) UNTIL s.eot OR (s.class = Texts.Name) & (s.s = "pos");
RETURN (s.class = Texts.Name) & (s.s = "pos")
END CheckErrors;
PROCEDURE MarkErrors*(f: TextFrames.Frame; logPos: LONGINT);
(** mark errors in frame f starting in log text at logPos ? *)
VAR s: Texts.Scanner; pos, delta: LONGINT; err: INTEGER; error: BOOLEAN;
BEGIN
DeleteErrElems(f.text);
IF CheckErrors(s, logPos) THEN
delta := 0;
LOOP
s.line := 0;
REPEAT Texts.Scan(s) UNTIL s.eot OR (s.line # 0) OR (s.class = Texts.Int);
IF s.eot OR (s.line # 0) THEN EXIT END;
pos := s.i;
Texts.Scan(s); error := (s.s = "err") OR (s.s = "pc");
REPEAT Texts.Scan(s) UNTIL s.eot OR (s.line # 0) OR (s.class = Texts.Int);
IF s.eot OR (s.line # 0) THEN EXIT END;
err := SHORT(s.i);
(* display errors, but warnings only if it's the Analyzer *)
IF error OR (compiler = "Analyzer.Analyze") THEN InsertErrAt(f.text, pos+delta, err); INC(delta) END;
REPEAT Texts.Scan(s) UNTIL s.eot OR (s.line # 0)
END
END
END MarkErrors;
PROCEDURE ErrCheck*(e: Texts.Elem): BOOLEAN;
BEGIN RETURN e IS Elem
END ErrCheck;
(** Handler *)
PROCEDURE HandleCall(f: TextFrames.Frame; pos: LONGINT; new: BOOLEAN);
VAR s: Texts.Scanner; par: Oberon.ParList; res, i, j: INTEGER; msg: ARRAY 128 OF CHAR;
BEGIN
Texts.OpenScanner(s, f.text, pos); Texts.Scan(s);
IF (s.class = Texts.Name) & (s.line = 0) THEN
i := -1;
REPEAT INC(i) UNTIL (i = s.len) OR (s.s[i] = ".");
j := i;
REPEAT INC(j) UNTIL (j >= s.len) OR (s.s[j] = ".");
IF (j >= s.len) & (s.s[i] = ".") THEN
NEW(par); par.frame := f; par.text := f.text; par.pos := pos+s.len;
Oberon.Call(s.s, par, new, res);
IF res # 0 THEN Host.CallError(s.s, res, msg); Str(msg); Ln END
END
END
END HandleCall;
PROCEDURE Handle*(f: Display.Frame; VAR msg: Display.FrameMsg);
VAR
tf, ff: TextFrames.Frame;
t, sel: Texts.Text; loc: TextFrames.Location; copyOver: Oberon.CopyOverMsg;
r: Texts.Reader; handled, done, keepSel: BOOLEAN; ch: CHAR; x, y: INTEGER;
pos, beg, end, len, time: LONGINT; keySum: SET;
PROCEDURE PartialFolds(text: Texts.Text; b, e: LONGINT): BOOLEAN;
CONST leftMode = {FoldElems.colLeft, FoldElems.expLeft, FoldElems.tempLeft, FoldElems.findLeft};
VAR level: INTEGER;
BEGIN
level := 0; Texts.OpenReader(r, text, b); Texts.ReadElem(r);
WHILE (r.elem # NIL) & (Texts.Pos(r) <= e) DO
IF r.elem IS FoldElems.Elem THEN
IF r.elem(FoldElems.Elem).mode IN leftMode THEN INC(level) ELSE DEC(level) END
END;
Texts.ReadElem(r)
END;
RETURN level # 0
END PartialFolds;
PROCEDURE ThisSubFrame(x, y: INTEGER): Display.Frame;
VAR sf: Display.Frame;
BEGIN
sf := f.dsc;
WHILE (sf # NIL) & ((x < sf.X) OR (x >= sf.X+sf.W) OR (y < sf.Y) OR (y >= sf.Y+sf.H)) DO sf := sf.next END;
RETURN sf
END ThisSubFrame;
BEGIN
tf := f(TextFrames.Frame);
IF keyHandle # NIL THEN keyHandle(tf, msg) END;
t := tf.text; handled := TRUE;
WITH msg: Oberon.InputMsg DO
IF (msg.id = Oberon.track) & (msg.X >= tf.X+tf.barW) & (ThisSubFrame(msg.X, msg.Y) = NIL) THEN
IF ML IN msg.keys THEN
Oberon.PassFocus(Viewers.This(tf.X, tf.Y)); TextFrames.TrackCaret(tf, x, y, keySum);
IF (keySum = {ML, MM}) & tf.hasCar THEN
Oberon.GetSelection(sel, beg, end, time);
IF time >= 0 THEN
Texts.Save(sel, beg, end, wr.buf); len := end-beg; pos := tf.carloc.pos;
Texts.Insert(tf.text, pos, wr.buf); TextFrames.SetCaret(tf, pos+len);
IF CaretVisible(tf, pos+len) THEN TextFrames.SetSelection(tf, pos, pos+len) END
END
ELSIF (keySum = {ML, MR}) & tf.hasCar & (tf.carloc.pos < tf.text.len) THEN
Oberon.GetSelection(sel, beg, end, time);
IF time >= 0 THEN
Texts.OpenReader(r, tf.text, tf.carloc.pos); Texts.Read(r, ch);
Texts.ChangeLooks(sel, beg, end, {0, 1, 2}, r.fnt, r.col, r.voff)
END
ELSIF (keySum = {ML}) & tf.hasCar THEN (* clear selection with left double click *)
IF Oberon.Time() < lastCarSet+delay THEN TextFrames.RemoveSelection(tf)
ELSE lastCarSet := Oberon.Time()
END
END
ELSIF MM IN msg.keys THEN
x := msg.X; y := msg.Y; pos := TextFrames.Pos(tf, x, y);
IF tf.hasSel & (tf.selbeg.pos <= pos) & (pos < tf.selend.pos) THEN MoveSelection(tf, x, y, msg.keys, done)
ELSE done := FALSE
END;
IF ~done THEN
Texts.OpenReader(r, t, pos); Texts.ReadElem(r);
IF (r.elem = NIL) OR (Texts.Pos(r) # pos+1) THEN (* no elem at this position *)
TextFrames.TrackWord(tf, x, y, pos, keySum); keySum := keySum+msg.keys;
IF (keySum = {MM}) OR (keySum = {MM, ML}) THEN HandleCall(tf, pos, keySum = {MM, ML})
ELSIF keySum = {MM, MR} THEN OpenCall(tf, x, y, pos)
END
ELSE handled := FALSE
END
END
ELSIF MR IN msg.keys THEN
TrackSelection(tf, msg.X, msg.Y, msg.keys);
IF (msg.keys = {MM, MR}) & tf.hasSel & ~PartialFolds(tf.text, tf.selbeg.pos, tf.selend.pos) THEN
copyOver.text := tf.text; copyOver.beg := tf.selbeg.pos; copyOver.end := tf.selend.pos;
len := copyOver.end-copyOver.beg;
IF (Oberon.FocusViewer IS MenuViewers.Viewer) & (Oberon.FocusViewer.dsc.next # NIL)
& (Oberon.FocusViewer(MenuViewers.Viewer).dsc.next IS TextFrames.Frame) THEN
ff := Oberon.FocusViewer.dsc.next(TextFrames.Frame); pos := ff.carloc.pos
ELSE ff := NIL
END;
Oberon.FocusViewer.handle(Oberon.FocusViewer, copyOver);
IF (ff # NIL) & CaretVisible(ff, pos+len) THEN TextFrames.SetSelection(ff, pos, pos+len) END
ELSIF (msg.keys = {ML, MR}) & tf.hasSel & ~PartialFolds(tf.text, tf.selbeg.pos, tf.selend.pos) THEN
Oberon.PassFocus(Viewers.This(tf.X, tf.Y));
Texts.Delete(tf.text, tf.selbeg.pos, tf.selend.pos); TextFrames.SetCaret(tf, tf.selbeg.pos)
END
ELSE handled := FALSE
END
ELSIF (msg.id = Oberon.consume) & tf.hasCar THEN
loc := tf.carloc; pos := loc.pos;
CASE msg.ch OF
| CR: msg.ch := LF; handled := FALSE (* switch CR <-> LF *)
| LF: msg.ch := CR; handled := FALSE
| BS, CtrlD: IF pos < t.len THEN Texts.Delete(t, pos, pos+1); TextFrames.SetCaret(tf, pos) END
| CtrlN: msg.ch := DnArrow; handled := FALSE
| CtrlP: msg.ch := UpArrow; handled := FALSE
| DnArrow, CtrlN:
(* IF loc.y-loc.dy <= tf.Y+tf.bot THEN (* at bottom of f *)
TextFrames.Show(tf, TextFrames.Pos(tf, loc.x, tf.Y+tf.bot+tf.H-tf.top));
TextFrames.SetCaret(tf, TextFrames.Pos(tf, loc.x+1, loc.y))
ELSE
y := loc.y-loc.dy;
REPEAT pos := TextFrames.Pos(tf, loc.x+1, y); DEC(y)
UNTIL (pos # loc.pos) OR (pos >= t.len) OR (y <= tf.Y+tf.bot);
TextFrames.SetCaret(tf, pos)
END
TextFrames.LocatePos(tf, t.len, loc2);
LOOP
y := loc.y+1;
REPEAT DEC(y); pos := TextFrames.Pos(tf, loc.x+1, y)
UNTIL (pos # loc.pos) OR (pos >= t.len) OR (y <= tf.Y+tf.bot);
IF pos # loc.pos THEN TextFrames.SetCaret(tf, pos); EXIT (* position found *)
ELSIF (pos >= t.len) OR (y <= loc2.y) THEN EXIT (* last line, no chance of finding one *)
END;
y := tf.Y+tf.H;
REPEAT DEC(y); TextFrames.LocateLine(tf, y, loc) UNTIL loc.org # tf.org;
TextFrames.Show(tf, loc.org);
TextFrames.LocatePos(tf, tf.carloc.pos, loc)
END
| UpArrow, CtrlP:
IF loc.org = tf.org THEN (* top of frame *)
IF tf.org > 0 THEN
pos := tf.org-1; TextFrames.Show(tf, pos);
TextFrames.SetCaret(tf, TextFrames.Pos(tf, loc.x+1, tf.Y+tf.H))
END
ELSE (* not at top *)
y := loc.y+loc.dy;
REPEAT pos := TextFrames.Pos(tf, loc.x+1, y); INC(y)
UNTIL (pos # loc.pos) OR (y >= tf.Y+tf.H);
TextFrames.SetCaret(tf, pos)
END
| CtrlT:
IF pos > 1 THEN (* exchange this with previous char *)
Texts.Save(t, pos-2, pos-1, wr.buf);
Texts.Delete(t, pos-2, pos-1); Texts.Insert(t, pos-1, wr.buf);
TextFrames.SetCaret(tf, pos)
END
| CtrlF:
IF pos < t.len THEN (* move one word forward *)
Texts.OpenReader(r, t, pos); Texts.Read(r, ch);
WHILE ~r.eot & (ch <= " ") & (ch # Texts.ElemChar) DO Texts.Read(r, ch) END;
IF r.eot THEN pos := t.len
ELSE
IF ~InWordSet(ch) THEN pos := Texts.Pos(r)
ELSE
REPEAT Texts.Read(r, ch) UNTIL r.eot OR ~InWordSet(ch);
IF r.eot THEN pos := t.len ELSE pos := Texts.Pos(r)-1 END;
TextFrames.LocatePos(tf, pos, loc);
IF loc.y <= tf.Y THEN TextFrames.Show(tf, pos) END; (* at bottom of f *)
END
END;
TextFrames.SetCaret(tf, pos)
END
| CtrlB:
IF pos > 0 THEN (* move one word backward *)
Texts.OpenReader(r, t, pos);
REPEAT BackRead(r, t, ch) UNTIL (Texts.Pos(r) = 0) OR (ch > " ") OR (ch = Texts.ElemChar);
IF Texts.Pos(r) = 0 THEN pos := 0
ELSE
IF ~InWordSet(ch) THEN pos := Texts.Pos(r)
ELSE
REPEAT BackRead(r, t, ch) UNTIL (Texts.Pos(r) = 0) OR ~InWordSet(ch);
IF Texts.Pos(r) = 0 THEN pos := 0 ELSE pos := Texts.Pos(r)+1 END
END
END;
IF pos < tf.org THEN TextFrames.Show(tf, pos) END;
TextFrames.SetCaret(tf, pos)
END
| CtrlE:
IF pos < t.len THEN (* move to end of (next) line *)
Texts.OpenReader(r, t, pos); Texts.Read(r, ch);
IF ~r.eot & (ch = CR) THEN Texts.Read(r, ch) END;
WHILE ~r.eot & (ch # CR) DO Texts.Read(r, ch) END;
IF r.eot THEN TextFrames.SetCaret(tf, t.len) ELSE TextFrames.SetCaret(tf, Texts.Pos(r)-1) END
END
| CtrlW:
IF pos > 0 THEN (* move to beginning of (previous) line *)
IF pos = loc.org THEN TextFrames.LocatePos(tf, pos-1, loc) END;
TextFrames.SetCaret(tf, loc.org)
END
| CtrlK: (* delete to end of line *)
Texts.OpenReader(r, t, pos);
REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = CR);
IF Texts.Pos(r) = pos+1 THEN Texts.Delete(t, pos, pos+1) ELSE Texts.Delete(t, pos, Texts.Pos(r)-1) END;
TextFrames.SetCaret(tf, pos)
| CtrlX: (* move selection to caret position *)
Oberon.GetSelection(sel, beg, end, time);
IF time >= 0 THEN MoveTextStretch(sel, tf, beg, end, pos) END
| CtrlZ:
IF pos < t.len THEN (* delete forward to non-char *)
Texts.OpenReader(r, t, pos); Texts.Read(r, ch);
WHILE ~r.eot & (ch <= " ") & (ch # Texts.ElemChar) DO Texts.Read(r, ch) END;
IF r.eot THEN end := t.len
ELSE
IF ~InWordSet(ch) THEN end := Texts.Pos(r)
ELSE
REPEAT Texts.Read(r, ch) UNTIL r.eot OR ~InWordSet(ch);
IF r.eot THEN end := t.len ELSE end := Texts.Pos(r)-1 END
END
END;
Texts.Delete(t, pos, end); TextFrames.SetCaret(tf, pos)
END
ELSE handled := FALSE
END (* CASE msg.ch ... *)
ELSE handled := FALSE
END (* IF msg.id = ... *)
| msg: Oberon.CopyOverMsg DO (* allow copy over only if text has no partial folds in it *)
IF ~tf.hasCar OR ~PartialFolds(msg.text, msg.beg, msg.end) THEN handled := FALSE END
ELSE handled := FALSE
END; (* WITH msg: ... *)
IF ~handled THEN
IF (msg IS TextFrames.UpdateMsg) & (msg(TextFrames.UpdateMsg).id = Texts.replace) & tf.hasSel THEN
keepSel := TRUE; beg := tf.selbeg.pos; end := tf.selend.pos
ELSE keepSel := FALSE
END;
TextFrames.Handle(tf, msg);
IF ~tf.hasSel & keepSel THEN TextFrames.SetSelection(tf, beg, end) END
END
END Handle;
(** Commands *)
PROCEDURE OpenModViewer(scanName: ARRAY OF CHAR; scanClass: INTEGER; VAR f: TextFrames.Frame);
VAR
v: Viewers.Viewer; t: Texts.Text;
name, extension: LongName; x, y: INTEGER;
BEGIN
OpenText(t, name, scanName, scanClass, empty[0], ext[0, 0], ext[0, 1]);
Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y);
f := TextFrames.NewText(t, 0);
Extension(name, extension);
IF extension = ext[0, 1] THEN
v := MenuViewers.New(MenuFrame(name, TextMenu, TextMenuNum), f, TextFrames.menuH, x, y)
ELSE
v := MenuViewers.New(MenuFrame(name, DefMenu, DefMenuNum), f, TextFrames.menuH, x, y)
END;
v.dsc.handle := Handle; v.dsc.next.handle := Handle
END OpenModViewer;
PROCEDURE Open*; (** (name | "^") Open a user viewer containing a text *)
VAR s: Texts.Scanner; f: TextFrames.Frame;
BEGIN
ScanFirst(s); OpenModViewer(s.s, s.class, f); SearchIdent(f)
END Open;
PROCEDURE SysOpen*; (** [defY] (name | "^") Open a system viewer at defY *)
VAR
v: Viewers.Viewer; t: Texts.Text;
s: Texts.Scanner; name: LongName; x, defY, y: INTEGER;
default: BOOLEAN;
BEGIN
ScanFirst(s);
IF s.class = Texts.Int THEN (* read desired Y-coordinate *)
defY := SHORT(s.i); default := TRUE; Oberon.Par.pos := Texts.Pos(s)-1; ScanFirst(s)
ELSE default := FALSE
END;
OpenText(t, name, s.s, s.class, empty[1], ext[1, 0], ext[1, 1]);
Oberon.AllocateSystemViewer(Oberon.Mouse.X, x, y);
IF default THEN y := defY END;
v := MenuViewers.New(MenuFrame(name, ToolMenu, ToolMenuNum),
TextFrames.NewText(t, 0), TextFrames.menuH,x,y);
v.dsc.handle := Handle; v.dsc.next.handle := Handle
END SysOpen;
PROCEDURE OpenWide*; (** (name | "^") Open a user viewer containing a text spanning whole display *)
VAR
v: Viewers.Viewer; t: Texts.Text; s: Texts.Scanner;
name: LongName; x, y: INTEGER;
BEGIN
ScanFirst(s); OpenText(t, name, s.s, s.class, empty[2], ext[2, 0], ext[2, 1]);
Oberon.OpenTrack(Oberon.UserTrack(Oberon.Mouse.X), Oberon.DisplayWidth(Oberon.Mouse.X));
Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y);
v := MenuViewers.New(MenuFrame(name, WideMenu, WideMenuNum),
TextFrames.NewText(t, 0), TextFrames.menuH, x, y);
v.dsc.handle := Handle; v.dsc.next.handle := Handle;
SearchIdent(v.dsc.next(TextFrames.Frame))
END OpenWide;
PROCEDURE OpenAscii*; (** (name | "^") Open a user viewer containing a text using a fixed spaced font *)
VAR
v: Viewers.Viewer; t: Texts.Text; s: Texts.Scanner;
name: LongName; x, y: INTEGER;
BEGIN
ScanFirst(s); OpenText(t, name, s.s, s.class, empty[3], ext[3, 0], ext[3, 1]);
Texts.ChangeLooks(t, 0, t.len, {0}, Fonts.This(AsciiFont), 0, 0);
Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y);
v := MenuViewers.New(MenuFrame(name, AsciiMenu, AsciiMenuNum),
TextFrames.NewText(t, 0), TextFrames.menuH, x, y);
v.dsc.handle := Handle; v.dsc.next.handle := Handle;
SearchIdent(v.dsc.next(TextFrames.Frame))
END OpenAscii;
PROCEDURE Error*; (** Show next error after caret *)
VAR f: TextFrames.Frame; v: Viewers.Viewer; pos: LONGINT; e: Texts.Elem; r: Texts.Reader;
BEGIN
IF Oberon.Par.vwr.dsc = Oberon.Par.frame THEN (* called from menu frame *)
IF (Oberon.Par.frame.next # NIL) & (Oberon.Par.frame.next IS TextFrames.Frame) THEN
f := Oberon.Par.frame.next(TextFrames.Frame)
ELSE f := NIL
END
ELSE
v := Oberon.MarkedViewer();
IF (v.dsc # NIL) & (v.dsc.next # NIL) & (v.dsc.next IS TextFrames.Frame) THEN f := v.dsc.next(TextFrames.Frame)
ELSE f := NIL
END
END;
IF f # NIL THEN
IF f.hasCar THEN pos := f.carloc.pos ELSE pos := 0 END;
IF f.hasCar & (pos > 0) THEN (* delete error elem before caret *)
Texts.OpenReader(r, f.text, pos-1); Texts.ReadElem(r);
IF (r.elem # NIL) & (Texts.ElemPos(r.elem) = pos-1) & (ErrCheck(r.elem)) THEN
Texts.Delete(f.text, pos-1, pos); DEC(pos)
END
END;
FoldElems.FindElem(f.text, pos, ErrCheck, e);
IF e # NIL THEN
pos:=Texts.ElemPos(e);
(*TextFrames.*)Show(f, pos); e(Elem).pos := pos; UpdateErr(e(Elem));
Oberon.PassFocus(Viewers.This(f.X, f.Y)); TextFrames.SetCaret(f, pos+1)
ELSIF f.hasCar THEN TextFrames.RemoveCaret(f)
END
END
END Error;
PROCEDURE Compile*; (** [options] | "*" | "^" | {fileName [options]} Compile viewer in main frame with options
or marked viewer or list of filenames with options *)
VAR
f: TextFrames.Frame; menuT: Texts.Text; s: Texts.Scanner; v: Viewers.Viewer;
len: LONGINT; options: Name; fileName: LongName; error: BOOLEAN;
PROCEDURE Comp(frame: TextFrames.Frame; text: Texts.Text; name: ARRAY OF CHAR);
VAR
vwr: MenuViewers.Viewer; logLen: LONGINT; x, y, h: INTEGER;
this, prev: Element; ext: Name; errorFile: LongName; res: INTEGER; sc: Texts.Scanner;
empty: BOOLEAN;
BEGIN
COPY(DefErrFile, errorFile);
IF (name # "") OR (frame = NIL) THEN
(* no compile command yet or compile command supplied, check extension *)
empty := compiler = "";
IF empty THEN COPY(defComp, compiler) END;
Extension(name, ext); this := SearchPair(ext, prev);
IF this # NIL THEN
IF empty THEN COPY(this.compiler, compiler) END;
COPY(this.errFile, errorFile)
END
END;
errT := TextFrames.Text(errorFile);
PrepareText(text);
IF frame = NIL THEN (* create temporary viewer *)
x := Display.Width-1; y := Display.Bottom; h := Viewers.minH; Viewers.minH := 1;
vwr := MenuViewers.New(TextFrames.NewMenu("", ""),
TextFrames.NewText(text, 0), TextFrames.menuH, x, y
);
Oberon.Pointer.X := x; Oberon.Pointer.Y := y;
Viewers.minH := h;
viewerComp := TRUE
ELSE DeleteErrElems(text); viewerComp := FALSE
END;
(* create new parameter text for compiler *)
Oberon.Par.text := TextFrames.Text(""); Oberon.Par.pos := 0;
Ch("*"); Str(options); Texts.Append(Oberon.Par.text, wr.buf);
Str(compiler); Ch(" "); Str(options); Texts.Append(Oberon.Log, wr.buf);
Append(".Compile", compiler); (* extend compiler command, if necessary *)
logLen := Oberon.Log.len;
Oberon.Call(compiler, Oberon.Par, FALSE, res);
IF res = 0 THEN
IF frame # NIL THEN MarkErrors(frame, logLen)
ELSE error := CheckErrors(sc, logLen)
END
END;
RestoreText;
IF frame = NIL THEN
Viewers.Close(vwr);
IF error THEN
OpenModViewer(name, Texts.Name, frame);
PrepareText(frame.text);
MarkErrors(frame, logLen);
RestoreText;
Oberon.Pointer.X := frame.X+frame.W DIV 2; Oberon.Pointer.Y := frame.Y+frame.H DIV 2;
Error
END
END
END Comp;
BEGIN
menuT := NIL;
IF Oberon.Par.vwr.dsc = Oberon.Par.frame THEN (* called from menu frame *)
IF (Oberon.Par.frame.next # NIL) & (Oberon.Par.frame.next IS TextFrames.Frame) THEN
f := Oberon.Par.frame.next(TextFrames.Frame);
menuT := Oberon.Par.frame(TextFrames.Frame).text (* menu text *)
END
ELSE (* allow XE.Compile * ... *)
ScanFirst(s);
IF (s.class = Texts.Char) & (s.c = "*") & (s.line = 0) THEN
Oberon.Par.pos := Texts.Pos(s);
v := Oberon.MarkedViewer();
IF (v IS MenuViewers.Viewer) & (v.dsc.next # NIL) & (v.dsc.next IS TextFrames.Frame) THEN
f := v.dsc.next(TextFrames.Frame);
menuT := v.dsc(TextFrames.Frame).text (* menu text *)
END
END
END;
IF menuT # NIL THEN
compiler := ""; fileName := "";
IF menuT # Oberon.Par.text THEN (* called from an element *)
ScanFirst(s);
IF (s.class = Texts.Name) & (s.line = 0) THEN (* get compiler override name *)
COPY(s.s, compiler); Texts.Scan(s)
END;
GetOptions(s, options)
END;
IF compiler = "" THEN
Texts.OpenScanner(s, menuT, 0); Texts.Scan(s);
IF s.class = Texts.Name THEN COPY(s.s, fileName) END
END;
len := menuT.len;
Oberon.DrawCursor(Oberon.Pointer, Oberon.Star, f.X, f.Y); Oberon.FadeCursor(Oberon.Pointer);
Comp(f, f.text, fileName);
IF len # menuT.len THEN (* text was stored and got an UpdateMsg -> ! char in menu text *)
Texts.Delete(menuT, menuT.len-1, menuT.len)
END;
Error (* show first error, if any *)
ELSE (* compile file list: {fileName [options] } ~ *)
ScanFirst(s); error := FALSE;
WHILE (s.class = Texts.Name) & ~error DO
COPY(s.s, fileName); Texts.Scan(s); GetOptions(s, options);
compiler := "";
Comp(NIL, TextFrames.Text(fileName), fileName)
END
END
END Compile;
PROCEDURE List*; (** List compiler, extension, errorfile set *)
VAR this: Element;
PROCEDURE WriteExt(cmd: ARRAY OF CHAR; i: INTEGER);
BEGIN Str(cmd); Str(" - "); Str(empty[i]); Str(" *."); Str(ext[i, 0]); Str(" *."); Str(ext[i, 1]); Ln
END WriteExt;
BEGIN
Str("XE.List"); Ln;
Str("OpenCmd - "); Str(openCmd); Ln;
WriteExt("Open", 0); WriteExt("SysOpen", 1); WriteExt("OpenWide", 2); WriteExt("OpenAscii", 3);
Str("Compiler"); Ln;
IF defComp # "" THEN Str(defComp); Str(" - * - "); Str(DefErrFile); Ln END;
this := root;
WHILE this # NIL DO
Str(this.compiler ); Str(" - *."); Str(this.ext); Str(" - "); Str(this.errFile); Ln;
this := this.next
END
END List;
PROCEDURE Defaults*; (** Clear compiler, extension, errorfile set and load default assignments from configuration file *)
VAR t: Texts.Text; s: Texts.Scanner; new, this, prev: Element;
PROCEDURE ScanExt(cmd: ARRAY OF CHAR; i: INTEGER);
BEGIN
IF (s.class = Texts.Name) & (s.s = cmd) THEN
Texts.Scan(s);
IF (s.class = Texts.Name) OR (s.class = Texts.String) THEN
COPY(s.s, empty[i]); Texts.Scan(s)
END;
IF (s.class = Texts.Name) OR (s.class = Texts.String) THEN
COPY(s.s, ext[i, 0]); Texts.Scan(s)
END;
IF (s.class = Texts.Name) OR (s.class = Texts.String) THEN
COPY(s.s, ext[i, 1]); Texts.Scan(s)
END
END
END ScanExt;
BEGIN
root := NIL;
defComp := DefComp; openCmd := DefOpenCmd;
empty[0] := Empty0; ext[0, 0] := Ext00; ext[0, 1] := Ext01;
empty[1] := Empty1; ext[1, 0] := Ext1; ext[1, 1] := Ext1;
empty[2] := Empty0; ext[2, 0] := Ext00; ext[2, 1] := Ext01;
empty[3] := Empty3; ext[3, 0] := Ext30; ext[3, 1] := Ext31;
t := TextFrames.Text(ConfigurationName);
IF t.len # 0 THEN
Texts.OpenScanner(s, t, 0); Texts.Scan(s);
IF (s.class = Texts.Name) & (s.s = "OpenCmd") THEN
Texts.Scan(s);
IF (s.class = Texts.Name) OR (s.class = Texts.String) THEN
COPY(s.s, openCmd); Texts.Scan(s)
END
END;
ScanExt("Open", 0); ScanExt("SysOpen", 1); ScanExt("OpenWide", 2); ScanExt("OpenAscii", 3);
IF (s.class = Texts.Name) & (s.s = "Compiler") THEN
Texts.Scan(s);
WHILE ~s.eot & (s.class = Texts.Name) DO
NEW(new); COPY(s.s, new.compiler); Texts.Scan(s);
IF (s.class = Texts.Char) & (s.c = "*") THEN defComp := new.compiler
ELSIF s.class = Texts.Name THEN
COPY(s.s, new.ext); Texts.Scan(s);
IF s.class = Texts.Name THEN
COPY(s.s, new.errFile); errT := TextFrames.Text(new.errFile);
IF errT.len = 0 THEN errT := TextFrames.Text(DefErrFile); COPY(DefErrFile, new.errFile) END
ELSE errT := TextFrames.Text(DefErrFile); COPY(DefErrFile, new.errFile)
END;
this := SearchPair(new.ext, prev); (* check for duplicates *)
IF this = NIL THEN new.next := root; root := new (* new entry *)
ELSIF this.compiler # new.compiler THEN (* new entry for existing extension -> remove this *)
IF this = root THEN new.next := root.next; root := new
ELSE new.next := this.next; prev.next := new
END
END
END;
Texts.Scan(s)
END
END
END
END Defaults;
PROCEDURE GetHandler*; (** install XE.Handle in Oberon.Par.frame.handle, if Oberon.Par.pos = GetHandlerKey *)
BEGIN
IF (Oberon.Par # NIL) & (Oberon.Par.pos = GetHandlerKey) & (Oberon.Par.frame # NIL) THEN
Oberon.Par.frame.handle := Handle
END
END GetHandler;
BEGIN
Texts.OpenWriter(wr); errFnt := Fonts.This(ErrFont); first := TRUE;
cleanup.text := NIL;
NEW(cleanup.task); cleanup.task.safe := FALSE; cleanup.task.handle := RestoreText; cleanup.task.time := 0;
Defaults;
delay := Host.TimeUnit DIV 2 (* 500 ms *)
END XE.